#include "auxiliar.h"

static int threadsAtivas = 0;

void *atendeConexao(void *arg){
   	char    buf[1000], dadosOri[22], proto[3], buftemp[300];       /* buffer for string the server sends */
    int *sp;
    int i, a, s, n, cp, retorno;
    char *end, lendo;
    Comando comando;
    
    //Somente necessário para a lógica do GET ALL
    MYSQL_RES* res;
    MYSQL_ROW row;
    int rowsEncontradas = 0;
    char *T_horaI_B, *T_horaF_B;

    sp=&((atende *) arg)->endereco;
    s=*sp;
    end = ((atende *) arg)->dadosOrigem;

    // Guarda quem esta chamando.
    for( cp = 0; cp < 22 ; dadosOri[cp] = *(end++), cp++);
       
    threadsAtivas++;
    fflush(stdout); // Esvaziando todo o buffer do stdout.

    /* Zerando as estruturas e buffers */
    memset((char *)&comando,0,sizeof(comando)); 
    memset(buf,0, 1000);
    proto[0] = proto[1] = proto[2] = '\0';

    n = recv(s, buf, sizeof(buf), 0);
    
    // Se n > 0 então recebeu alguma requisiçao
    if (n > 0) {

        // Obter o protocolo.
        for ( i=0 ; (lendo=buf[i])!=';' && lendo != '\0' ; i++ )
            proto[i] = lendo;        
        i++;
        comando.protocolo = atoi(proto);

        switch(comando.protocolo){
            case LOGIN:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;  

                    printComando(dadosOri,&comando,"LOGIN");
					memset(buf,0, 1000);

                    inicializarBanco();
                    retorno = fazerLogin(comando.usuario,comando.senha);
                    fecharBanco();

                    if ( retorno > 0 )
                        strcpy(buf,"200;Faz login lá campeão. Tudo OK");
                    else if ( retorno == 0 )
                        strcpy(buf,"400;Usuário/Senha inválidos");
                    else if ( retorno < 0 )
                        strcpy(buf,"404;Problemas de conexão com o banco.");
                    
                    send(s, buf, strlen(buf), 0);

                    break;
                }
            case ADD:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaI[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaF[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.msg[a] = lendo;

                    printComando(dadosOri,&comando,"ADD");

					memset(buf,0, 1000);

                    inicializarBanco();
                    adicionarEvento(&comando, &buf);
                    fecharBanco();

                    send(s, buf, strlen(buf), 0);
                    break;
                }
            case REMOVEALL:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;

                    printComando(dadosOri,&comando,"REMOVE ALL");

					memset(buf,0, 1000);

                    inicializarBanco();
                    removerTodosEventos(&comando, buf);
                    fecharBanco();

                    send(s, buf, strlen(buf), 0);
                    break;
                }
            case REMOVE:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaI[a] = lendo;
                    i++;

                    printComando(dadosOri,&comando,"REMOVE");

					memset(buf,0, 1000);

                    inicializarBanco();
                    removerEvento(&comando, buf);
                    fecharBanco();

                    send(s, buf, strlen(buf), 0);
                    break;
                }
            case UPDATE:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaI[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaF[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.msg[a] = lendo;

                    printComando(dadosOri,&comando,"UPDATE");

					memset(buf,0, 1000);

                    inicializarBanco();
                    atualizarEvento(&comando, buf);
                    fecharBanco();

                    send(s, buf, strlen(buf), 0);
                    break;
                }
            case GET:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.horaI[a] = lendo;
                    i++;

                    printComando(dadosOri,&comando,"GET");

					memset(buf,0, 1000);

                    handlerReqGet(&comando, buf);

                    send(s, buf, strlen(buf), 0);

                    break;
                }
            case GETALL:
                {
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.usuario[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.senha[a] = lendo;
                    i++;
                    for ( a=0 ; (lendo=buf[i])!=';' ; i++, a++ )
                        comando.data[a] = lendo;  
                    i++;

                    printComando(dadosOri,&comando,"GET ALL");

					memset(buf,'\0', 1000);
                    handlerReqGetAll(&comando, buf, &res);

                    if ( strlen(buf) != 0 ){
                        send(s, buf, strlen(buf), 0);
                        liberarResultSet(res);
                        break;
                    }

                    rowsEncontradas = (int) mysql_num_rows(res);

                    if(rowsEncontradas == 0){
                        sprintf(buf,
                                "400;Caro Sr. <%s>, voce NAO tem eventos no dia <%s>.",
                                 comando.usuario, comando.data);

                        send(s, buf, strlen(buf), 0);
                        system("sleep 0.2");

                    } else {
                        sprintf(buf,
                                "200;%d;%s\0",
                                 rowsEncontradas, comando.data);
                        send(s, buf, strlen(buf), 0);
                        system("sleep 0.2");

                        memset(buf,'\0', 1000);
                        for( i = 0; (row = mysql_fetch_row(res)) != NULL; i++){
                            memset(buftemp, '\0', 300);
                            decompoeHora( row[2], &T_horaI_B);
                            decompoeHora( row[3], &T_horaF_B);

                            if ( i%4 == 0 )
                                sprintf(buftemp, "%s;%s;%s", T_horaI_B,T_horaF_B,row[4]);
                            else
                                sprintf(buftemp, ";%s;%s;%s", T_horaI_B,T_horaF_B,row[4]);

                            strcat(buf,buftemp);
          
                            if ( i%4 == 3 ){
                                send(s, buf, strlen(buf), 0);
                                system("sleep 0.2");
                                memset(buf,'\0', 1000);
                            }
                        }

                        if ( i%4 != 0 ){
                            send(s, buf, strlen(buf), 0);
                        }
                    }

                    liberarResultSet(res);
                    break;
                }
            // TODO Tratar aqui se o protocolo não for definido, Isso não deve ocorrer NUNCA!.
            default:
                {
                    fprintf(stderr, "Protocolo Inválido.\n");
                    break;
                } 
        }
    }

    puts("FINALIZANDO THREAD\n");
   	closesocket(s);
    fflush(stdout);
    threadsAtivas--;
    pthread_exit(NULL);
}




    /*------------------------------------------------------------------------
    * Program:   server
    *
    * Purpose:   allocate a socket and then repeatedly execute the following:
    *              (1) wait for the next connection from a client
    *              (2) send a short message to the client
    *              (3) close the connection
    *              (4) go back to step (1)
    *
    * Syntax:    server [ port ]
    *
    *               port - protocol port number to use
    *
    * Note:      The port argument is optional. If no port is specified,
    *            the server uses the default given by PROTOPORT.
    *
    *------------------------------------------------------------------------
    */
int main(int argc, char *argv[]) {
    struct protoent *ptrp; /* pointer to a protocol table entry     */
    struct sockaddr_in sad; /* structure to hold server’s address */
    struct sockaddr_in cad; /* structure to hold client’s address */
    int     sd, sd2;         /* socket descriptors                  */
    int     port, incPort;   /* protocol port number                */
   	int     alen;            /* length of address                   */
    atende  origem;
 
 	pthread_attr_t * thAttr = NULL;
  	pthread_t tid; 

   	#ifdef WIN32
   		WSADATA wsaData;
   		WSAStartup(0x0101, &wsaData);
   	#endif
   	memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
   	sad.sin_family = AF_INET;         /* set family to Internet     */
   	sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address   */
   	/* Check command-line argument for protocol port and extract    */
   	/* port number if one is specified. Otherwise, use the default */
  	/* port value given by constant PROTOPORT                       */
    	
	if (argc > 1) {                 /* if argument specified        */
    	port = (int) atoi(argv[1]);   /* convert argument to binary    */
    } else {
        port = PROTOPORT;       /* use default port number       */
    }
 
   	if (port > 0)                   /* test for illegal value       */
    		sad.sin_port = htons((u_short)port);
    	else {                          /* print error message and exit */
    		fprintf(stderr,"bad port number %s\n",argv[1]);
		exit(1);
    	}

    	/* Map TCP transport protocol name to protocol number */
    	if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) {
    		fprintf(stderr, "cannot map \"tcp\" to protocol number");
    		exit(1);
    	}

    	/* Create a socket */
    	sd = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
    	if (sd < 0) {
    		fprintf(stderr, "socket creation failed\n");
    		exit(1);
    	}

    	/* Bind a local address to the socket */
    	if (bind(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
    		fprintf(stderr,"bind failed\n");
    		exit(1);
    	}

    	/* Specify size of request queue */
    	if (listen(sd, QLEN) < 0) {
    		fprintf(stderr,"listen failed\n");
    		exit(1);
    	}

    	/* Main server loop - accept and handle requests */
    	while (1) {
    		alen = sizeof(cad);
    		if ( (origem.endereco=accept(sd, (struct sockaddr *)&cad, &alen)) < 0) {
    			fprintf(stderr, "accept failed\n");
    			exit(1);
    		}

            sprintf(origem.dadosOrigem,"%s:%d", (char *)inet_ntoa(cad.sin_addr), ntohs(cad.sin_port));
    		pthread_create(&tid, thAttr, atendeConexao, (void*)&origem); 
   		
   	}
	return 0;
}


